using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Zsa.Emulation.Memory
{
    /// <summary>
    /// A memory stream.
    /// </summary>
    public class MemoryBankStream : IMemoryStream
    {
        #region Fields
        /// <summary>
        /// The name of the bank or stream.
        /// </summary>
        private string _Name;
        /// <summary>
        /// The memory stream.
        /// </summary>
        private MemoryStream _Stream;
        /// <summary>
        /// The memory bytes.
        /// </summary>
        protected byte[] _Buffer;
        /// <summary>
        /// The internal offset for the stream.
        /// </summary>
        protected int _InternalOffset = 0;
        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="T:MemoryBankStream"/> class.
        /// </summary>
        /// <param name="size">The size.</param>
        /// <param name="name">The name.</param>
        internal MemoryBankStream(int size, string name)
            : this(size, name, 0)
        {
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="T:MemoryBankStream"/> class.
        /// </summary>
        /// <param name="size">The size.</param>
        internal MemoryBankStream(int size, string name, int offset)
        {
            _Name = name;
            _Buffer = new byte[size];
            _Stream = new MemoryStream(_Buffer, true);
            _InternalOffset = offset;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:MemoryBankStream"/> class.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="name">The name.</param>
        internal MemoryBankStream(MemoryStream stream, string name)
            : this ( stream, name,0)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:MemoryBankStream"/> class.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="name">The name.</param>
        internal MemoryBankStream(MemoryStream stream, string name, int offset)
        {
            _Name = name;
            _Stream = stream;
            _Buffer = stream.GetBuffer();
            _InternalOffset = offset;
        }
        #endregion Constructor

        #region Methods
        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public byte Read(int address)
        {
            return _Buffer[address + _InternalOffset];
        }

        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public byte Read(ushort address)
        {
            return _Buffer[address + _InternalOffset];
        }

        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public ushort ReadUInt16(int address)
        {
            return (ushort)(_Buffer[address + _InternalOffset + 1] << 8 | _Buffer[address + _InternalOffset]);
        }

        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public ushort ReadUInt16(ushort address)
        {
            return (ushort)(_Buffer[address + _InternalOffset + 1] << 8 | _Buffer[address + _InternalOffset]);
        }

        /// <summary>
        /// Reads the specified buffer.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <param name="address">The address.</param>
        /// <param name="count">The count.</param>
        public void Read(byte[] buffer, int address, int count)
        {
            //most expensive call in application
            int offsetAddress = address + _InternalOffset;
            Buffer.BlockCopy(_Buffer, offsetAddress, buffer, 0, count);
        }

        /// <summary>
        /// Writes the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <param name="romByte">The rom byte.</param>
        public virtual void Write(int address, byte romByte)
        {
            _Buffer[address + _InternalOffset] = romByte;
        }

        /// <summary>
        /// Writes the specified buffer.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <param name="address">The address.</param>
        /// <param name="count">The count.</param>
        /// <returns></returns>
        public virtual void Write(byte[] buffer, int address, int count)
        {
            _Stream.Seek(address + _InternalOffset, SeekOrigin.Begin);
            _Stream.Write(buffer, 0, count);
        }
        #endregion

        #region Properties 
        /// <summary>
        /// Gets the length of the stream.
        /// </summary>
        /// <value>The size.</value>
        public int Length
        {
            get{
                return (int)_Stream.Length;
            }
        }

        /// <summary>
        /// Gets or sets the internal offset.
        /// </summary>
        /// <value>The internal offset.</value>
        internal int InternalOffset
        {
            get
            {
                return _InternalOffset;
            }
            set
            {
                _InternalOffset = value;
            }
        }

        /// <summary>
        /// Gets the internal buffer.
        /// </summary>
        /// <value>The internal buffer.</value>
        public byte[] InternalBuffer
        {
            get
            {
                return _Buffer;
            }
        }

        #endregion
    }
}
